#include <stdio.h>
#include <map>
#include "ServerBootstrap.h"
#include "ReactorThreadPool.h"
#include "Log.h"
#include "Codec.h"

using namespace dsth;
using std::map;

class CharServer {
public:
    typedef map<string, Connection*> ChatList;
    typedef map<int, string> ConnMap;
    CharServer(EventLoop* loop, const InetSocketAddress& addr, ReactorThreadPool* pool) 
        : server_(loop, addr) {
        server_.SetOnConnection(std::make_pair(this, &CharServer::OnConnect));
        server_.SetOnMessage(std::make_pair(&codec_, &Codec::OnMessage));
        server_.SetOnDisConnection(std::make_pair(this, &CharServer::OnDisConnection));
        codec_.SetMessageHandler(std::make_pair(this, &CharServer::OnMessage));
        codec_.SetErrorHandler(std::make_pair(this, &CharServer::OnMessageError));
    }

    void OnConnect(Connection* conn) {
        printf("A new connection.\n");
    }

    void OnMessage(Connection* conn, message_t* msg, Timestamp time) {
        printf("TO:%s, FROM:%s, Text:%s\n", msg->to.c_str(), msg->from.c_str(), msg->chat_text.c_str());
        if (msg->to == "LOGIN") {
            // empty name user
            if (msg->from.size() == 0) {
                printf("empty name user\n");
                conn->ShutDown();
                return;
            }
            // name duplicate.
            if (chat_list_.find(msg->from) != chat_list_.end()) {
                printf("%s name duplicate.\n", msg->from.c_str());
                conn->ShutDown();
                return;
            }
            printf("Add %s to list.\n", msg->from.c_str());
            chat_list_[msg->from] = conn;
            conn_map_[conn->GetId()] = msg->from;
            ResponseClient(conn, msg->from, "Login Successful");
        }
        else {
            if (chat_list_.find(msg->from) == chat_list_.end()) {
                printf("%s has not signed in.\n", msg->from.c_str());
                conn->ShutDown();
                return;
            }

            ChatList::iterator it = chat_list_.find(msg->to);
            if (it == chat_list_.end()) {
                string response(msg->to);
                response.append(" doesn't online.");
                ResponseClient(conn, msg->from, response.c_str());
            }
            else {
                Connection* conn_to = chat_list_[msg->to];
                codec_.SendMessage(conn_to, *msg);
                ResponseClient(conn, msg->from, "Message Sent Successful.");
            }
        }
    }

    void OnDisConnection(Connection* conn) {
        ConnMap::iterator it = conn_map_.find(conn->GetId());
        if (it != conn_map_.end()) {
            printf("%s is quiting.", it->second.c_str());
            size_t n = chat_list_.erase(it->second);
            assert(n == 1);
            n = conn_map_.erase(conn->GetId());
            assert(n == 1);
        }
        else {
            printf("OnDisConnection, connection not found.\n");
        }
    }

    void OnMessageError(Connection* conn, Timestamp time) {
        ConnMap::iterator it = conn_map_.find(conn->GetId());
        if (it != conn_map_.end()) {
            printf("%s has sent a error message.", it->second.c_str());
            size_t n = chat_list_.erase(it->second);
            assert(n == 1);
            n = conn_map_.erase(conn->GetId());
            assert(n == 1);

        }
        else {
            printf("OnMessageError.\n");
        }
        conn->ShutDown();
    }

    void Run() { server_.Run(); }

private:
    void ResponseClient(Connection* conn, const string& client, const char* text) {
        message_t msg;
        msg.to.assign(client);
        msg.from.assign("Chat Server");
        msg.chat_text.assign(text);
        codec_.SendMessage(conn, msg);
    }

    ServerBootstrap server_;
    Codec codec_;
    ChatList chat_list_;
    ConnMap  conn_map_;
};

#ifdef IS_WINDOWS
static void ExitForWindows() {
    printf("::WSACleanup()\n");
    ::WSACleanup();
}
#endif

int main(int argc, char** argv) {
    NewLogger("./chat_server.log", Logger::E_DEBUG);
#ifdef IS_WINDOWS
    WSADATA wsaData;
    
    int err = WSAStartup( MAKEWORD(2,2), &wsaData );
    if( err != NO_ERROR )
        printf("Error at WSAStartup\n");
    ::atexit(ExitForWindows);
#endif
    
    ReactorThreadPool pool(1);
    InetSocketAddress addr(10084);
    EventLoop loop;
    CharServer char_server(&loop, addr, &pool);
    char_server.Run();
    loop.Loop();
    return 0;
}
